#include "bkwidget.h"
#include "ui_bkwidget.h"
#include <QLabel>
#include <QPainterPath>
#include <QSettings>
#include <transtext.h>
#include <QDesktopServices>
#include <QFileDialog>
#include <QStorageInfo>
#include <QFileInfo>
#include <windows.h>
#include <tlhelp32.h>
#include <QDebug>
#include <QProcess>
#include <QStandardPaths>
#include <tlhelp32.h>
#include <iostream>
#include <QThread>
#include <Winerror.h>
#include <QTimer>
#include <QTranslator>
#include <iostream>
QTranslator trans;
QString g_deleteApplicationDir = "";
QString g_deleteApplicationExe = "";



/// 打开和关闭成对使用
bool open_Log(std::shared_ptr<QFile> filePtr, QTextStream* textStream) {
    filePtr->setFileName(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/ANYCUBIC/test.log");
    if(filePtr->open(QIODevice::Append | QIODevice::Text)) {
        textStream->setDevice(filePtr.get());
        return true;
    }

    return false;
}

void close_Log(std::shared_ptr<QFile> filePtr) {
    filePtr->close();
}

bool write_Log(std::initializer_list<QString> list) {
    QFile tmpFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/ANYCUBIC/test.log");
    if(tmpFile.open(QIODevice::Append | QIODevice::Text)) {
        QTextStream textStream(&tmpFile);
        for (const auto& str : list) {
            textStream << str;
        }
        textStream << "\n";
        tmpFile.close();
        return true;
    }

    return false;
}

bool delete_log() {
    return QFile::remove(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/ANYCUBIC/test.log");
}



void Worker::doWork() {

    std::shared_ptr<QFile> file =std::make_shared<QFile>();
    QTextStream textStream;
    open_Log(file, &textStream);
    //QDir::removeRecursively
    if(g_deleteApplicationDir.isEmpty()) {
        emit workResult(false);
        return;
    }

    QDir dir(g_deleteApplicationDir);
    dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDot | QDir::NoDotDot |QDir::NoDotAndDotDot);
    dir.setSorting(QDir::DirsFirst);
    QFileInfoList list = dir.entryInfoList();
    auto iter = std::find_if(list.begin(), list.end(), [](QFileInfo info){
        return (info.fileName() == g_deleteApplicationExe);
    });

    if(iter == list.end() || g_deleteApplicationDir.isEmpty()) {
        emit workResult(false);
        return;
    }

    QList<QFileInfo> removeList;
    removeList.clear();

    QFileInfo exeInfo(QApplication::applicationFilePath());
    textStream << "\ncurrent uninstall exe path: : " << exeInfo.fileName();

    for(auto& info : list) {
        if(info.isDir() || (info.isFile() && !info.filePath().contains(exeInfo.fileName()))) {
            textStream << "\ncurrent path: " << info.fileName();
            removeList << info;
        }
    }

    // remove
    int totalNumber = removeList.size();
    int cnt = 0;
    while (removeList.size() > 0) {
        auto deleteInfo = *removeList.begin();
        textStream << "\nremover begin" <<"\n";
        if(deleteInfo.isDir()) {
            auto path = deleteInfo.absoluteFilePath();
            QDir tmpDir(path);
            if(tmpDir.removeRecursively())
                textStream << path <<" is delete success\n";
            else {
                textStream << path <<" is delete failed\n";
                cnt++;
            }

        } else {
            auto path = deleteInfo.absoluteFilePath();
            QFile deleteFile(path);
            if(deleteFile.remove())
                textStream << path <<" is delete success\n";
            else {
                textStream << path <<" is delete failed\n";
                cnt++;
            }
        }

        textStream << "\nremover end" << "\n";
        removeList.erase(removeList.begin());
        int progress = (totalNumber - removeList.size()) * 1.0 / totalNumber * 100;
        emit currentProcess(progress);
    }

    if(cnt > 0 || totalNumber == 0) {
         textStream << " uninstaller is failed!" << "\n";
        close_Log(file);
        emit workResult(false);
    }
    else {
         textStream << " uninstaller is success!" << "\n";
         close_Log(file);
       emit workResult(true);
    }
}

BkWidget::BkWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::BkWidget)
{
    ui->setupUi(this);
     // 设置无边框和透明背景
    setWindowFlags(Qt::FramelessWindowHint);
    setAttribute(Qt::WA_TranslucentBackground);
    QGraphicsDropShadowEffect *windowShadow = new QGraphicsDropShadowEffect(this);
    // 阴影多少，留出部分阴影的空间
    windowShadow->setBlurRadius(24);
    windowShadow->setOffset(0);
    ui->frame->setGraphicsEffect(windowShadow);
    setFixedSize(660 + 24*2, 480 + 24*2);
    delete_log();
    initSettings();
    initData();
    initConnection();
    initView();
    if(g_deleteApplicationDir.isEmpty() || m_productName.isEmpty() || g_deleteApplicationExe.isEmpty()) {
        qDebug() << "delete directory: " << g_deleteApplicationDir << "product name: " << m_productName;
        write_Log({"delete directory: ", g_deleteApplicationDir, "product name: ", m_productName});
        ui->uninstallBtn->setEnabled(false);
    }
}

BkWidget::~BkWidget()
{
    delete ui;
}


void BkWidget::initConnection()
{
    connect(ui->stackedWidget, &QStackedWidget::currentChanged, this, [this](){
        if(ui->stackedWidget->currentWidget() == ui->page_uninstalling)
            ui->closeBtn->setEnabled(false);
        else
            ui->closeBtn->setEnabled(true);
    });

    connect(ui->closeBtn, &QPushButton::clicked, this, [this](){
        close();
    });

    connect(ui->uninstallBtn, &QPushButton::clicked, this, [this](){
        // step 4:
        if(isProcessRunning()) {
            m_messageBox->display(kProgramRunning, pos(), this->size());
            return;
        }

        deleteApplicationFile();
    });

    connect(ui->completedBtn, &QPushButton::clicked, this, [this](){
        deleteRegPath();
        selfDelete();
        close();
    });

    connect(ui->uninstallFailedBtn, &QPushButton::clicked, this, [this](){
        deleteRegPath();
        selfDelete();
        close();
    });

    connect(ui->noUninstallBtn, &QPushButton::clicked, this, [this](){
        close();
    });
}

void BkWidget::readReg()
{
    QString region = (m_productRegion == 0) ? "anycubic_installer" : "anycubic_installer_cn";
    QString regPath = "HKEY_CURRENT_USER\\Software\\" + region;
    QSettings settings(regPath, QSettings::NativeFormat);
    settings.setIniCodec("UTF-16");
    g_deleteApplicationDir = settings.value("install_directory", g_deleteApplicationDir).toString();
    write_Log({__FUNCTION__, "install_directory: ", g_deleteApplicationDir});
    m_productName = settings.value("display_name", m_productName).toString();
    write_Log({__FUNCTION__, "display_name: ", m_productName});
    g_deleteApplicationExe = settings.value("run_name", g_deleteApplicationExe).toString();
    write_Log({__FUNCTION__, "run_name: ", g_deleteApplicationExe});
    m_isntallAppId = settings.value("application_id", m_isntallAppId + "_is1").toString();
    write_Log({__FUNCTION__, "application_id: ", m_isntallAppId});
}

void BkWidget::initSettings()
{
    // test
#if TEST_ARGMENTS
    m_productName = "AnycubicPhotonWorkshop";
    m_isntallAppId = "{C48D4F03-E59D-475F-B34D-E618A500C118}_is1";
    g_deleteApplicationDir = "C:\\Program Files\\AnycubicPhotonWorkshop";
#else
    auto arglist = QApplication::arguments();
    if(arglist.size() > 3) {
        m_isntallAppId = arglist[1];
        m_productName = QString::fromUtf8(arglist[2].toUtf8());
        m_productRegion = arglist[3].toInt();
        write_Log({"convert arglist: ", m_isntallAppId, m_productName, QString::number(m_productRegion)});
    }

    readReg();
    QString langFilePath = QApplication::applicationDirPath() + "/translation_en.qm";
    if(m_productRegion == 1) {
           langFilePath = QApplication::applicationDirPath() + "/translation_zh.qm";
    }


    bool bRet = trans.load(langFilePath);
    if(bRet){
        qApp->installTranslator(&trans);
    }

#endif

}

void BkWidget::initData()
{
    // test end
    setWindowTitle(m_productName);
    auto tempAAA = m_productName;
    m_productName = QStringLiteral("安装包制作新版方案");
    auto temp = TransString::instance()->uninstallStart.replace("XXX", m_productName);
    ui->labelUninstallStart->setText(temp);
    temp = TransString::instance()->uninstalling.replace("XXX", m_productName);
    ui->labelUninstalling->setText(temp);
    temp = TransString::instance()->uninstallEnd.replace("XXX", m_productName);
    ui->labelCompleted->setText(temp);
    temp = TransString::instance()->uninstallError1.replace("XXX", m_productName);
    ui->labelUninstallFailed->setText(temp);
    ui->labelUninstallFailed_2->setText(TransString::instance()->uninstallError2);
    ui->uninstallBtn->setText(TransString::instance()->uninstallOK);
    ui->noUninstallBtn->setText(TransString::instance()->uninstallCancel);
    ui->uninstallFailedBtn->setText(TransString::instance()->uninstallComplete);
    ui->completedBtn->setText(TransString::instance()->uninstallComplete);
    ui->stackedWidget->setCurrentIndex(0);
    if(m_messageBox == nullptr) {
        m_messageBox = new MessageInfomation(this);
    }

    m_productName = tempAAA;
}

void BkWidget::initView()
{
    ui->progressBar->setTextVisible(false);
}

bool BkWidget::isProcessRunning() {
    bool bFind = false;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        qDebug() << "Failed to create snapshot";
        return bFind;
    }

    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    if (!Process32First(hSnapshot, &pe32)) {
        qDebug() << "Failed to get first process";
        CloseHandle(hSnapshot);
        return bFind;
    }

    do {
        std::wstring name = pe32.szExeFile;
        if(QString::fromStdWString(name) ==g_deleteApplicationExe)
            bFind = true;
    } while (Process32Next(hSnapshot, &pe32));

    CloseHandle(hSnapshot);
    return bFind;
}

void BkWidget::getDeskAndAppPath(wchar_t* DeskTop, wchar_t* AppData)
{
    HKEY hKey;
    LONG result;
    DWORD dwSize = 0;
    std::wstring strValue;
    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, QString("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders").toStdWString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
    if (result == ERROR_SUCCESS) {
        result = RegQueryValueEx(hKey, QString("Common Desktop").toStdWString().c_str(), 0, nullptr, nullptr, &dwSize);
        if (result == ERROR_SUCCESS) {
            strValue.resize(dwSize);
            RegQueryValueEx(hKey, QString("Common Desktop").toStdWString().c_str(), 0, NULL, (LPBYTE)strValue.data(), &dwSize);
            lstrcpy(DeskTop, strValue.c_str());
        }

        RegCloseKey(hKey);
    }

    result = RegOpenKeyEx(HKEY_CURRENT_USER, QString("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders").toStdWString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
    if (result == ERROR_SUCCESS) {
        result = RegQueryValueEx(hKey, QString("AppData").toStdWString().c_str(), 0, nullptr, nullptr, &dwSize);
        if (result == ERROR_SUCCESS) {
            strValue.resize(dwSize);
            RegQueryValueEx(hKey, QString("AppData").toStdWString().c_str(), 0, NULL, (LPBYTE)strValue.data(), &dwSize);
            lstrcpy(AppData, strValue.c_str());
            lstrcat(AppData, QString("\\Microsoft\\Internet Explorer\\Quick Launch").toStdWString().c_str());
        }

        RegCloseKey(hKey);
    }
}

IShellFolder* BkWidget::getIShellFolderByPath(QString path)
{
    IShellFolder* m_ShellFolderTopMost = NULL;
    HRESULT hr = SHGetDesktopFolder(&m_ShellFolderTopMost);
    if (FAILED(hr)){
        return nullptr;
    }
    IShellFolder* m_pFolder;
    LPITEMIDLIST pidlWorkDir = NULL;
    hr = m_ShellFolderTopMost->ParseDisplayName(NULL, NULL, (wchar_t*)path.toStdWString().c_str(), NULL, &pidlWorkDir, NULL);
    if (FAILED(hr)) {
        return   nullptr;
    }

    m_ShellFolderTopMost->BindToObject(pidlWorkDir, NULL, IID_IShellFolder, (LPVOID*)&m_pFolder);
    if (m_ShellFolderTopMost)m_ShellFolderTopMost->Release();
    return m_pFolder;
}

bool BkWidget::getIEunmIDList(IShellFolder* m_pShellFolder, wchar_t* path)
{
    if (!m_pShellFolder) return false;
    LPENUMIDLIST pEnum = nullptr;
    HRESULT hr = m_pShellFolder->EnumObjects(0, SHCONTF_NONFOLDERS, &pEnum);
    if (FAILED(hr))
    {
        return false;
    }

    LPITEMIDLIST m_pItem = NULL;
    ULONG   m_ulwork = 0;
    while (pEnum->Next(1, &m_pItem, &m_ulwork) == S_OK)
    {
        QString strTempPath = QString::fromStdWString(path);
        WIN32_FIND_DATA ffd;
        SHGetDataFromIDList(m_pShellFolder, m_pItem, SHGDFIL_FINDDATA, &ffd, sizeof(WIN32_FIND_DATA));
        strTempPath +=QString("\\").toStdWString();
        strTempPath += QString::fromStdWString(ffd.cFileName);
        QFileInfo infomation(strTempPath);
        if (m_productName.size() > 0 && infomation.baseName() == m_productName) {
            QFile file(strTempPath);
            if(file.exists())
                file.remove();
            write_Log({"delelte IShellFolder: ", strTempPath});
        }
    }

    return TRUE;
}
// 删除快捷方式
bool BkWidget::delDesktopIShellFolder()
{
    wchar_t deskPath[8196] = { 0 };
    wchar_t appdataPath[8196] = { 0 };
    getDeskAndAppPath(deskPath, appdataPath);
    IShellFolder* deskShell = getIShellFolderByPath(QString::fromWCharArray(deskPath));
    getIEunmIDList(deskShell, deskPath);
    return true;
}

void BkWidget::deleteRegPath()
{
    QString guid = m_isntallAppId;
    delDesktopIShellFolder();
    HKEY hKey;
    LONG result;
    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, (QString(REG_PATH)).toStdWString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
    if (ERROR_SUCCESS == result) {
        RegDeleteKey(hKey, guid.toStdWString().c_str());
        RegCloseKey(hKey);
    }

    wchar_t* strPath = nullptr;
    if (SUCCEEDED(::SHGetKnownFolderPath(FOLDERID_CommonPrograms, 0, nullptr, &strPath))) {
        QString commonPath = QString::fromStdWString(strPath);
        QDir dir(commonPath);
        if (!dir.exists())
            return;

        dir.setFilter(QDir::Dirs);
        const QFileInfoList list = dir.entryInfoList();
        foreach (const QFileInfo &fileInfo, list) {
            if(m_productName.size() > 0 && fileInfo.fileName() == m_productName && fileInfo.isDir()) {
                qDebug() << fileInfo.filePath();
                write_Log({"need delete dir: ", fileInfo.filePath()});
                QDir dir(fileInfo.filePath());
                QString filePath = fileInfo.filePath() + "/" + fileInfo.fileName() + ".lnk";
                QFile linkFile(filePath);
                if(QFile::exists(filePath)){
                    write_Log({"delete dir: ", dir.dirName()});
                    dir.removeRecursively();
                }

                break;
            }
        }


        SHChangeNotify(SHCNE_RMDIR, SHCNF_PATH, g_deleteApplicationDir.toStdWString().c_str(), NULL);
    }
}

bool BkWidget::selfDelete() {
    QString tmpPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/uninstaller.bat";
    QFile tmpFile(tmpPath);
    if(tmpFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QFileInfo  info = QApplication::applicationFilePath();
        QTextStream text_stream(&tmpFile);
        text_stream.setCodec("UTF-8");
        text_stream << "@echo off" << "\n";
        text_stream << "chcp 65001" << "\n";
        text_stream << ":loop" << "\n";
        text_stream << "tasklist | find /i \"" << info.fileName() << "\" > nul" << "\n";
        text_stream << "if %errorlevel% equ 0 (" << "\n";
        text_stream << "timeout /t 0.01" << "\n";
        text_stream << "goto loop" << "\n";
        text_stream << ") else (" << "\n";
        text_stream << "del /f /s /q \"" << g_deleteApplicationDir << "/*.*" << "\"" << "\n";
        text_stream << "rd /s /q \"" << g_deleteApplicationDir << "\"" << "\n";
        text_stream << ")" << "\n";
        tmpFile.flush();
        tmpFile.close();
        QProcess process;
        process.setWorkingDirectory(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
        process.startDetached(tmpPath.toStdString().c_str(), QStringList());
        return true;
    }

    return false;
}

void BkWidget::deleteApplicationFile() {
    if(m_thread == nullptr) {
        m_thread = new QThread();
        m_worker.moveToThread(m_thread);
        QObject::connect(m_thread, &QThread::started, &m_worker, &Worker::doWork);

        QObject::connect(&m_worker, &Worker::currentProcess, this, [this](int value){
            if(ui->stackedWidget->currentWidget() != ui->page_uninstalling) {
                ui->stackedWidget->setCurrentWidget(ui->page_uninstalling);
            }
            ui->progressBar->setValue(value);
            qDebug() << "value: " << value;
            update();
        });

        QObject::connect(&m_worker, &Worker::workResult, this, [this](bool result){
             m_thread->quit();
            if(result && ui->stackedWidget->currentWidget() != ui->page_completed) {
                ui->stackedWidget->setCurrentWidget(ui->page_completed);
            } else if(!result && ui->stackedWidget->currentWidget() != ui->page_failed) {
                ui->stackedWidget->setCurrentWidget(ui->page_failed);
            };
        });
    }

    m_thread->start();
}

void BkWidget::mousePressEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton && event->pos().y() <= 50) {
        // 检查是否点击在标题栏区域
        m_dragging = true;
        m_dragPosition = event->globalPos() - frameGeometry().topLeft();
    }
}

void BkWidget::mouseMoveEvent(QMouseEvent *event) {
    if (m_dragging && (event->buttons() & Qt::LeftButton)) {
        move(event->globalPos() - m_dragPosition);
    }
}

void BkWidget::mouseReleaseEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {
        m_dragging = false;
    }
}

bool BkWidget::eventFilter(QObject *obj, QEvent *event) {


    return QWidget::eventFilter(obj, event);
}
